package com.sanji.capture_audio.thread

import android.content.Context
import android.media.*
import android.net.Uri
import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract
import android.telecom.Call
import android.util.Log
import com.sanji.capture_audio.utils.Preferences
import java.io.File
import java.io.IOException

/**
 * 从电话输入设备录制音频
 *
 * @param context 用于访问 [AudioManager]，对象中会保存该引用。
 * @param listener 用于发送完成通知。监听器会在此线程中调用，而非主线程。
 * @param call 仅用于日志记录，不会保存。
 */
class RecorderThread(
    private val context: Context,
    private val listener: OnRecordingCompletedListener,
    call: Call,
) : Thread(RecorderThread::class.java.simpleName) {
    companion object {
        // private val TAG = "${RecorderThread::class.java.simpleName}/${Thread.currentThread().id}"
        private val TAG = "RECORDER_THREAD"
        private const val TIMEOUT = 500L
    }

    private val prefs = Preferences(context)

    // 线程状态
    @Volatile private var isCancelled = false
    private var recordFailed = false

    init {
        Log.i(TAG, "为通话创建录音线程: $call")
    }

    override fun run() {
        var success = false
        var errorMsg: String? = null

        try {
            Log.i(TAG, "录音线程已启动")

            if (isCancelled) {
                Log.i(TAG, "录音在线程开始前已被取消")
            } else {
                recordUntilCancelled()

                success = !recordFailed
            }
        } catch (e: Exception) {
            Log.e(TAG, "录音过程中出错", e)
            errorMsg = e.localizedMessage
        } finally {
            Log.i(TAG, "录音线程已完成")

            if (success) {
                listener.onRecordingCompleted(this)
            } else {
                listener.onRecordingFailed(this, errorMsg)
            }
        }
    }

    /**
     * 取消录音。这将在处理下一个最小缓冲区大小后停止录音。
     *
     * 如果在 [start] 之前调用，则不会初始化音频输入设备。
     */
    fun cancel() {
        isCancelled = true
    }

    /**
     * 持续录音直到调用 [cancel] 或发生音频录音错误。
     *
     * @throws Exception 如果无法找到电话输入设备或编码器配置失败
     * @return 录音文件名
     */
    private fun recordUntilCancelled(): String {
        Log.d(TAG, "开始录音过程")
        // 查找电话输入设备
        val audioManager = context.getSystemService(AudioManager::class.java)
        val telephonyInput = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)
            .find { it.type == AudioDeviceInfo.TYPE_TELEPHONY }
            ?: throw Exception("未找到电话输入音频设备")
        Log.d(TAG, "找到电话输入设备: $telephonyInput")

        // 使用应用私有目录保存录音文件，避免权限问题
        val externalFilesDir = context.getExternalFilesDir(null)
            ?: throw Exception("无法访问应用外部存储目录")
        Log.d(TAG, "应用外部存储目录: $externalFilesDir")
        
        // 确保目录存在
        if (!externalFilesDir.exists()) {
            if (!externalFilesDir.mkdirs()) {
                throw Exception("无法创建应用外部存储目录")
            }
        }

        // 创建录音文件
        val timestamp = System.currentTimeMillis()
        val fileName = "call_recording_$timestamp.mp4"
        val outputFile = File(externalFilesDir, fileName)
        Log.d(TAG, "录音文件路径: ${outputFile.absolutePath}")

        // 创建音频录制器
        val audioRecord = createAudioRecord(telephonyInput)
        Log.d(TAG, "成功创建AudioRecord")
        
        // 创建MediaMuxer (使用MUXER_OUTPUT_MPEG_4但是只写入音频轨道)
        val mediaMuxer = try {
            Log.d(TAG, "尝试创建MediaMuxer")
            MediaMuxer(outputFile.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
        } catch (e: Exception) {
            audioRecord.release()
            Log.e(TAG, "初始化MediaMuxer时出错", e)
            throw Exception("无法初始化录音功能，请检查存储权限: ${e.message}")
        }
        Log.d(TAG, "成功创建MediaMuxer")
        
        val mediaCodec = try {
            Log.d(TAG, "尝试创建编码器")
            createEncoder(audioRecord.format)
        } catch (e: Exception) {
            mediaMuxer.release()
            audioRecord.release()
            Log.e(TAG, "创建编码器时出错", e)
            throw e
        }
        Log.d(TAG, "成功创建编码器")

        try {
            Log.d(TAG, "启动编码器和录音器")
            mediaCodec.start()
            audioRecord.startRecording()

            if (audioRecord.recordingState != AudioRecord.RECORDSTATE_RECORDING) {
                throw Exception("录音设备启动失败，请检查录音权限")
            }
            Log.d(TAG, "编码器和录音器启动成功")

            try {
                Log.d(TAG, "开始录音循环")
                recordingLoop(audioRecord, mediaCodec, mediaMuxer)
                Log.d(TAG, "录音循环结束")
            } finally {
                Log.d(TAG, "停止录音")
                audioRecord.stop()
            }
        } finally {
            Log.d(TAG, "释放资源")
            try {
                audioRecord.release()
            } catch (e: Exception) {
                Log.w(TAG, "释放 AudioRecord 时出错", e)
            }
            
            try {
                mediaCodec.release()
            } catch (e: Exception) {
                Log.w(TAG, "释放 MediaCodec 时出错", e)
            }
            
            try {
                mediaMuxer.release()
            } catch (e: Exception) {
                Log.w(TAG, "释放 MediaMuxer 时出错", e)
            }
        }

        return fileName
    }

    /**
     * 从URI获取文件路径
     *
     * @param uri 文件URI
     * @return 文件路径字符串
     */
    private fun getFilePathFromUri(uri: Uri): String {
        return uri.path ?: throw Exception("无法从URI获取文件路径")
    }

    /**
     * 录音的主循环。
     *
     * 循环会一直运行直到调用 [cancel]。取消的大致时间是处理最小缓冲区大小所需的时间。
     *
     * @param audioRecord 必须已调用 [AudioRecord.startRecording]
     * @param mediaCodec 必须已调用 [MediaCodec.start]
     * @param mediaMuxer 用于写入输出文件
     *
     * @throws Exception 如果在音频录制或编码过程中发生错误
     */
    private fun recordingLoop(
        audioRecord: AudioRecord,
        mediaCodec: MediaCodec,
        mediaMuxer: MediaMuxer
    ) {
        var trackIndex = -1
        var muxerStarted = false
        var presentationTimeUs = 0L
        val sampleRate = audioRecord.format.sampleRate
        // 每个样本的字节数 (16位 = 2字节)
        val bytesPerSample = 2
        // 声道数 (这里假设是单声道)
        val channelCount = audioRecord.format.channelCount
        
        // 持续循环直到取消录音
        while (!isCancelled) {
            var waitForever = false

            // 获取编解码器的输入缓冲区
            val inputBufferId = mediaCodec.dequeueInputBuffer(TIMEOUT)
            if (inputBufferId >= 0) {
                val inputBuffer = mediaCodec.getInputBuffer(inputBufferId)!!
                
                // 从音频输入设备读取数据到输入缓冲区
                val n = audioRecord.read(inputBuffer, inputBuffer.capacity(), AudioRecord.READ_BLOCKING)
                
                // 检查是否读取成功
                if (n < 0) {
                    Log.w(TAG, "读取音频数据失败: $n")
                    if (n == AudioRecord.ERROR_INVALID_OPERATION) {
                        throw Exception("AudioRecord 无效操作")
                    } else if (n == AudioRecord.ERROR_BAD_VALUE) {
                        throw Exception("AudioRecord 错误值")
                    }
                }
                
                // 计算正确的presentation time
                // 基于已读取的样本数计算时间戳
                val framesRead = n / (bytesPerSample * channelCount)
                val newPresentationTimeUs = (framesRead * 1000000L) / sampleRate
                presentationTimeUs += newPresentationTimeUs
                
                // 检查是否已到达文件末尾
                val flags = if (n <= 0) {
                    Log.d(TAG, "正在处理最终缓冲区; 提交EOF")
                    waitForever = true
                    isCancelled = true
                    MediaCodec.BUFFER_FLAG_END_OF_STREAM
                } else {
                    0
                }
                
                // 将输入缓冲区提交给编解码器，使用正确的时间戳
                mediaCodec.queueInputBuffer(
                    inputBufferId,
                    0,
                    n,
                    presentationTimeUs,
                    flags,
                )
            } else {
                Log.w(TAG, "意外的输入缓冲区出队错误: $inputBufferId")
            }
            
            // 处理编解码器的输出
            val result = processOutput(mediaCodec, mediaMuxer, trackIndex, muxerStarted)
            trackIndex = result.first
            muxerStarted = result.second
        }
        
        // 录音结束后，确保处理完所有输出缓冲区
        processOutput(mediaCodec, mediaMuxer, trackIndex, muxerStarted)
    }

    /**
     * 处理编解码器的输出数据并写入到媒体混合器。
     *
     * @param mediaCodec 媒体编解码器对象，用于编码音频数据
     * @param mediaMuxer 媒体混合器对象，用于写入编码后的数据
     * @param trackIndex 轨道索引
     * @param muxerStarted 混合器是否已启动
     * @return Pair<Int, Boolean> 返回更新后的 trackIndex 和 muxerStarted 状态
     */
    private fun processOutput(
        mediaCodec: MediaCodec,
        mediaMuxer: MediaMuxer,
        trackIndex: Int,
        muxerStarted: Boolean
    ): Pair<Int, Boolean> {
        val bufferInfo = MediaCodec.BufferInfo()
        var currentTrackIndex = trackIndex
        var isMuxerStarted = muxerStarted

        // 持续处理输出缓冲区直到没有更多数据
        while (true) {
            val outputBufferId = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT)
            if (outputBufferId >= 0) {
                val buffer = mediaCodec.getOutputBuffer(outputBufferId)!!

                // 如果是第一个输出缓冲区，添加轨道到混合器
                if (!isMuxerStarted) {
                    val format = mediaCodec.outputFormat
                    Log.d(TAG, "添加轨道到媒体混合器，格式: $format")
                    try {
                        currentTrackIndex = mediaMuxer.addTrack(format)
                        mediaMuxer.start()
                        isMuxerStarted = true
                    } catch (e: IllegalStateException) {
                        Log.e(TAG, "添加轨道到媒体混合器时出错", e)
                        mediaCodec.releaseOutputBuffer(outputBufferId, false)
                        break
                    }
                }

                // 将编码后的音频数据写入媒体混合器
                if (isMuxerStarted && bufferInfo.size > 0) {
                    try {
                        mediaMuxer.writeSampleData(currentTrackIndex, buffer, bufferInfo)
                    } catch (e: Exception) {
                        Log.e(TAG, "写入样本数据时出错", e)
                        mediaCodec.releaseOutputBuffer(outputBufferId, false)
                        throw Exception("写入录音数据时出错: ${e.message}")
                    }
                }

                // 释放已处理的输出缓冲区
                mediaCodec.releaseOutputBuffer(outputBufferId, false)

                // 检查是否已到达流的末尾
                if (bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) {
                    Log.d(TAG, "收到EOF; 已完全处理")
                    break
                }
            } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                // 只有在 muxer 未启动时才处理格式更改
                if (!isMuxerStarted) {
                    val format = mediaCodec.outputFormat
                    Log.d(TAG, "输出格式更改为: $format")
                    try {
                        currentTrackIndex = mediaMuxer.addTrack(format)
                        mediaMuxer.start()
                        isMuxerStarted = true
                    } catch (e: IllegalStateException) {
                        Log.e(TAG, "添加轨道到媒体混合器时出错", e)
                        break
                    }
                }
            } else if (outputBufferId == MediaCodec.INFO_TRY_AGAIN_LATER) {
                break
            } else {
                Log.w(TAG, "意外的输出缓冲区出队错误: $outputBufferId")
                break
            }
        }
        
        return Pair(currentTrackIndex, isMuxerStarted)
    }

    /**
     * 创建并配置音频录制器
     *
     * @param telephonyInput 电话输入设备
     * @return 配置好的AudioRecord实例
     */
    private fun createAudioRecord(telephonyInput: AudioDeviceInfo): AudioRecord {
        val sampleRate = 44100
        val channelConfig = AudioFormat.CHANNEL_IN_MONO
        val audioFormat = AudioFormat.ENCODING_PCM_16BIT
        
        val minBufSize = AudioRecord.getMinBufferSize(
            sampleRate,
            channelConfig,
            audioFormat
        )
        
        if (minBufSize < 0) {
            throw Exception("查询最小缓冲区大小时失败: $minBufSize")
        }

        Log.d(TAG, "AudioRecord 最小缓冲区大小: $minBufSize")

        // 创建音频录制器
        val attributes = AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
            .build()
            
        val format = AudioFormat.Builder()
            .setSampleRate(sampleRate)
            .setChannelMask(channelConfig)
            .setEncoding(audioFormat)
            .build()
            
        val audioRecord = AudioRecord(
            MediaRecorder.AudioSource.VOICE_CALL,
            sampleRate,
            channelConfig,
            audioFormat,
            minBufSize * 2
        )

        Log.d(TAG, "AudioRecord 格式: ${audioRecord.format}")

        if (!audioRecord.setPreferredDevice(telephonyInput)) {
            throw Exception("设置首选输入设备失败")
        }

        return audioRecord
    }

    /**
     * 创建并配置音频编码器
     *
     * @param audioFormat 音频格式
     * @return 配置好的MediaCodec实例
     * @throws Exception 如果没有找到合适的编码器或配置失败
     */
    private fun createEncoder(audioFormat: AudioFormat): MediaCodec {
        val codecList = MediaCodecList(MediaCodecList.REGULAR_CODECS)
        
        val format = MediaFormat.createAudioFormat(
            MediaFormat.MIMETYPE_AUDIO_AAC,
            audioFormat.sampleRate,
            audioFormat.channelCount
        )
        format.setInteger(MediaFormat.KEY_BIT_RATE, 128000)
        format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC)
        
        val encoder = codecList.findEncoderForFormat(format)
            ?: throw Exception("未找到合适的音频编码器")
            
        Log.d(TAG, "音频编码器: $encoder")

        val codec = MediaCodec.createByCodecName(encoder)
        codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        
        return codec
    }

    interface OnRecordingCompletedListener {
        /**
         * 当录音成功完成时调用。
         *
         * @param thread 调用此方法的录音线程实例
         */
        fun onRecordingCompleted(thread: RecorderThread)

        /**
         * 当录音过程中发生错误时调用。
         *
         * @param thread 调用此方法的录音线程实例
         * @param errorMsg 错误信息描述
         */
        fun onRecordingFailed(thread: RecorderThread, errorMsg: String?)
    }
}